﻿using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization.Metadata;
using DimensionsHelper.Common.Contracts.Excel;
using DimensionsHelper.Common.Contracts.Excel.Options;

namespace DimensionsHelper.ExcelAddIn.Core;

public abstract class ExcelAddInBase<T, TTran> : IExcelAddIn 
    where T : class, IExcelAddInSettings, new()
    where TTran : class, IExcelAddInTransmission, new()
{
    protected ExcelAddInBase()
    {
        Settings = SettingService.Default;
    }


    protected abstract LocalSettingService<T> SettingService { get; }

    protected T Settings { get; set; }

    public bool IsDefaultSetting(string name) => SettingService.IsDefaultSetting(name);

    public IExcelAddInSettings GetSetting() => Settings;

    public IExcelAddInSettings GetDefaultSetting() => SettingService.Default;

    public string[] GetLocalSettingNames() => SettingService.SettingNames;

    public IExcelAddInSettings? GetLocalSetting(string name) => SettingService.Load(name);

    public bool UpdateSetting(IExcelAddInSettings settings)
    {
        if (settings is T typedSettings)
        {
            Settings = typedSettings;
            return true;
        }

        return false;
    }


    protected abstract JsonTypeInfo<TTran> TransmissionJsonTypeInfo { get; }


    public object? DeserializeTransmission(object data)
    {
        if (data is TTran typedSettings)
        {
            return typedSettings;
        }

        // JsonObject时，重新反序列化
        if (data is JsonObject jsonObject)
        {
            return jsonObject.Deserialize(TransmissionJsonTypeInfo);
        }

        // JsonElement时，重新反序列化
        if (data is JsonElement jsonElement)
        {
            return jsonElement.Deserialize(TransmissionJsonTypeInfo);
        }

        return null;
    }


    public bool Save(object settings)
    {
        return Save(ExcelAddInEnv.DefaultSettingName, settings, true);
    }


    public bool Save(string settingName, object settings, bool asDefault)
    {
        if (settings is not T typedSettings)
        {
            if (DeserializeTransmission(settings) is TTran tran)
            {
                typedSettings = (T)tran.ToSettings();
            }
            else
            {
                return false;
            }
        }

        SettingService.Save(settingName, typedSettings, asDefault);
        return true;
    }


    public void Save(string settingName, bool asDefault)
    {
        SettingService.Save(settingName, Settings, asDefault);
    }


    public abstract void Run(IExcelWorkbookOperator workbook);


    public abstract void BuildSettingSheet(IExcelWorkbookOperator workbook);


    // Utils

    protected static RangeStyleOptions DefaultStyle { get; } = new RangeStyleOptions
    {
        Font = new FontStyleOptions
        {
            FontSize = ExcelAddInEnv.FontSize,
            FontFamily = ExcelAddInEnv.FontFamily
        },
        VAlign = ExcelVAlign.Center
    };


    protected static RangeStyleOptions DefaultCenterStyle { get; } = new RangeStyleOptions
    {
        Font = new FontStyleOptions
        {
            FontSize = ExcelAddInEnv.FontSize,
            FontFamily = ExcelAddInEnv.FontFamily
        },
        HAlign = ExcelHAlign.Center,
        VAlign = ExcelVAlign.Center
    };


    protected static string MakeEnumValidationList<TEnum>() where TEnum : struct
    {
        var type = typeof(TEnum);
        var builder = new StringBuilder();

        if (type.IsEnum)
        {
            foreach (var field in type.GetEnumNames())
            {
                if (builder.Length > 0)
                {
                    builder.Append(',');
                }

                builder.Append(field.ToUpper());
            }
        }

        return builder.ToString();
    }


    protected static IExcelRangeOperator SetTextCenter(IExcelWorksheetOperator worksheet, int row, int column, string? text)
    {
        var cell = worksheet.GetRange(row, column);

        if (!string.IsNullOrEmpty(text))
        {
            cell.Value = text!;
        }

        cell.SetStyle(DefaultCenterStyle);
        return cell;
    }


    protected static IExcelRangeOperator SetDescriptionCenter(IExcelWorksheetOperator worksheet, int row, int column, string description)
    {
        var cell = worksheet.GetRange(row, column);
        cell.Value = description;
        cell.SetStyle(new RangeStyleOptions
        {
            Font = new FontStyleOptions
            {
                FontSize = ExcelAddInEnv.FontSize,
                FontFamily = ExcelAddInEnv.FontFamily,
                FontType = FontType.Bold,
            },
            HAlign = ExcelHAlign.Center,
            VAlign = ExcelVAlign.Center
        });

        return cell;
    }


    protected static IExcelRangeOperator SetListValidation(IExcelWorksheetOperator worksheet, int row, int column, string list, string defaultValue)
    {
        var cell = worksheet.GetRange(row, column);
        cell.SetListValidation(list);
        cell.Value = defaultValue;
        cell.SetStyle(DefaultCenterStyle);
        return cell;
    }


    protected static IExcelRangeOperator SetIntegerValidation(IExcelWorksheetOperator worksheet, int row, int column, int lower)
    {
        var cell = worksheet.GetRange(row, column);
        cell.SetIntegerGreaterEqualValidation(lower);
        cell.SetStyle(DefaultCenterStyle);
        return cell;
    }


    protected static IExcelRangeOperator SetIntegerValidation(IExcelWorksheetOperator worksheet, int startRow, int startColumn, 
        int endRow, int endColumn, int lower)
    {
        var cell = worksheet.GetRange(startRow, startColumn, endRow, endColumn);
        cell.SetIntegerGreaterEqualValidation(lower);
        cell.SetStyle(DefaultCenterStyle);
        return cell;
    }


    protected const string BooleanTrue = "TRUE";
    protected const string BooleanFalse = "FALSE";


    protected static IExcelRangeOperator SetBooleanValidation(IExcelWorksheetOperator worksheet, int row, int column, bool defaultValue)
    {
        var cell = worksheet.GetRange(row, column);
        cell.SetStyle(DefaultCenterStyle);
        cell.SetListValidation("TRUE,FALSE");
        cell.Value = defaultValue ? BooleanTrue : BooleanFalse;
        return cell;
    }

    protected static string? ReadAsString(object?[,] sheet, int row, int column)
    {
        if (row < sheet.GetLowerBound(0) || 
            row > sheet.GetUpperBound(0) || 
            column < sheet.GetLowerBound(1) ||
            column > sheet.GetUpperBound(1))
        {
            return null;
        }

        var value = sheet[row, column]?.ToString();
        return string.IsNullOrWhiteSpace(value) ? null : value;
    }


    protected static string? ReadAsString(IExcelWorksheetOperator sheet, int row, int column)
    {
        try
        {
            var text = sheet.GetRange(row, column).Value?.ToString();
            return string.IsNullOrWhiteSpace(text) ? null : text;
        }
        catch (Exception)
        {
            return null;
        }
    }


    protected static bool ReadAsBoolean(object?[,] sheet, int row, int column)
    {
        if (row > sheet.GetUpperBound(0) || column > sheet.GetUpperBound(1))
        {
            return false;
        }

        var cell = sheet[row, column];
        return cell?.ToString()?.ToUpper() == BooleanTrue;
    }


    protected static bool ReadAsBoolean(IExcelWorksheetOperator sheet, int row, int column)
    {
        try
        {
            return sheet.GetCellText(row, column)?.ToUpper() == BooleanTrue;
        }
        catch (Exception)
        {
            return false;
        }
    }


    protected static int ReadAsInteger(object?[,] sheet, int row, int column)
    {
        if (row > sheet.GetUpperBound(0) || column > sheet.GetUpperBound(1))
        {
            return 0;
        }

        var cell = sheet[row, column];
        return cell switch
        {
            null => 0,
            int integer => integer,
            float f => (int)f,
            double db => (int)db,
            _ => int.TryParse(cell.ToString(), out int result) ? result : 0,
        };
    }


    protected static int ReadAsInteger(IExcelWorksheetOperator sheet, int row, int column)
    {
        try
        {
            var val = sheet.GetRange(row, column).Value;
            return val switch
            {
                null => 0,
                int integer => integer,
                float f => (int)f,
                double db => (int)db,
                _ => int.TryParse(val.ToString(), out int result) ? result : 0,
            };
        }
        catch (Exception)
        {
            return 0;
        }
    }
    

    protected static TEnum ReadAsEnum<TEnum>(object?[,] sheet, int row, int column) where TEnum : struct
    {
        if (row > sheet.GetUpperBound(0) || column > sheet.GetUpperBound(1))
        {
            return default;
        }

        object? cell = sheet[row, column];

        if (cell == null)
        {
            return default;
        }

        string value = cell.ToString();
        return Enum.TryParse<TEnum>(value, true, out var result) ? result : default;
    }


    protected static TEnum ReadAsEnum<TEnum>(IExcelWorksheetOperator sheet, int row, int column) where TEnum: struct
    {
        try
        {
            object? value = sheet.GetRange(row, column).Value;

            if (value == null)
            {
                return default;
            }

            return Enum.TryParse<TEnum>(value.ToString(), true, out var result) ? result : default;
        }
        catch (Exception)
        {
            return default;
        }
    }

}
